
#pragma once

#include "ttg/errors.h"
#include "ttg/structures.h"
#include "ttg/extras.h"

using namespace System;
using namespace NUnit::Framework;
using namespace ttg::extras::serialization;

//Auxiliary object, which will be used for non-flat data serialization.
class TestObject :public ttg::structures::SerializableObject
{
	public:
		std::string name;
		int age;
	public:
		//Auxiliary.
		TestObject()
		{
			this->name = "Unknown";
			this->age = age;
		}
		TestObject(const char *name, int age)
		{
			this->name = name;
			this->age = age;
		}
		bool operator ==(const TestObject &object)
		{
			return this->age == object.age && this->name == object.name;
		}
		//SerializableObject member.
		virtual void serialize(Serializer &serializer)
		{
			serializer.serializeString(name, "name");
			serializer.serializeInt(age, "age");
		}
		//SerializableObject member.
		virtual void deserialize(DeSerializer &deserializer)
		{
			deserializer.deserializeString(name, "name");
			deserializer.deserializeInt(age, "age");
		}
};

namespace CommonTests
{
	namespace extras
	{
		namespace serialization
		{
			public ref class CommonSerializationTests
			{
				public:
					//Serializes diffrent simple values and compares deserialization results.
					static void ValueTypeSerialization(Serializer *serializer)
					{
						try
						{
							//Serializing.
							serializer->serializeInt(42, "int_value");
							serializer->serializeBool(true, "bool_value");
							serializer->serializeFloat(1.0f, "float_value");
							serializer->serializeDouble(2.0, "double_value");
							//Deserializing.
							DeSerializer *deserializer = serializer->createDeSerializer();
							int int_value;
							bool bool_value;
							float float_value;
							double double_value;
							deserializer->deserializeInt(int_value, "int_value");
							deserializer->deserializeBool(bool_value, "bool_value");
							deserializer->deserializeFloat(float_value, "float_value");
							deserializer->deserializeDouble(double_value, "double_value");
							SAFE_DELETE(deserializer);
							//Checking.
							Assert::AreEqual(42, int_value,
											 "ByteDeSerializer::deserializeInt has returned wrong value");
							Assert::AreEqual(true, bool_value,
											 "ByteDeSerializer::deserializeBool has returned wrong value");
							Assert::AreEqual(1.0f, float_value, 0.0f,
											 "ByteDeSerializer::deserializeFloat has returned wrong value");
							Assert::AreEqual(2.0, double_value, 0.0,
											 "ByteDeSerializer::deserializeDouble has returned wrong value");
						}
						catch (ttg::Error &err)
						{
							Assert::Fail(String::Format("Error has been thrown: {0}",
														gcnew String(err.toString())));
						}
					}

					//Serializes different strings and checks results.
					static void StringSerialization(Serializer *serializer)
					{
						try
						{
							//Serializing.
							serializer->serializeString(std::string("Hello world!"), "str1");
							serializer->serializeString("Another string", "str2");
							serializer->serializeString(NULL, "null_str");
							//Deserializing.
							std::string str1;
							std::string str2;
							std::string null_str;
							DeSerializer *deserializer = serializer->createDeSerializer();
							deserializer->deserializeString(str1, "str1");
							deserializer->deserializeString(str2, "str2");
							deserializer->deserializeString(null_str, "null_str");
							SAFE_DELETE(deserializer);
							//Checking.
							Assert::AreEqual("Hello world!", gcnew String(str1.c_str()),
											 "ByteDeSerializer::deserializeString() has returned wrong value");
							Assert::AreEqual("Another string", gcnew String(str2.c_str()),
											 "ByteDeSerializer::deserializeString() has returned wrong value");
							Assert::AreEqual("", gcnew String(null_str.c_str()),
											 "ByteDeSerializer::deserializeString() has returned wrong value");
						}
						catch (ttg::Error &err)
						{
							Assert::Fail(String::Format("Error has been thrown: {0}",
														gcnew String(err.toString())));
						}
					}

					//Serializes non-flat object.
					static void ObjectSerialization(Serializer *serializer)
					{
						try
						{
							//Serializing.
							TestObject vasya("Vasya", 17);
							TestObject petya("Petya", 42);
							serializer->serializeObject(&vasya, "vasya");
							serializer->serializeObject(&petya, "petya");
							serializer->serializeObject(NULL, "null_person");
							//Deserializing.
							TestObject new_vasya;
							TestObject *new_petya = new TestObject();
							TestObject *null_person = new TestObject();
							DeSerializer *deserializer = serializer->createDeSerializer();
							deserializer->deserializeObject(new_vasya, "vasya");
							deserializer->deserializeObject(new_petya, "petya");
							deserializer->deserializeObject(null_person, "null_person");
							SAFE_DELETE(deserializer);
							//Checking correctness.
							Assert::AreEqual(true, vasya == new_vasya,
											 "DeSerializer::deserializeObject() has returned wrong value");
							Assert::AreEqual(true, petya == *new_petya,
											 "DeSerializer::deserializeObject() has returned wrong value");
							Assert::AreEqual(true, null_person == NULL,
											 "DeSerializer::deserializeObject() has returned wrong value");
						}
						catch (ttg::Error &err)
						{
							Assert::Fail(String::Format("Error has been thrown: {0}",
														gcnew String(err.toString())));
						}
					}

					//Checks correctness of wrong-order deserialization.
					static void WrongOrderSerialization(Serializer *serializer)
					{
						try
						{
							//Serializing.
							serializer->serializeInt(12, "int_value");
							serializer->serializeFloat(10.0f, "float_value");
							//Deserializing.
							int int_value;
							float float_value;
							DeSerializer *deserializer = serializer->createDeSerializer();
							deserializer->deserializeFloat(float_value, "float_value");
							deserializer->deserializeInt(int_value, "int_value");
							Assert::Fail("Error should be thrown");
						}
						catch(ttg::Error &) {}
					}

					//Checks correctness of wrong-name deserialization.
					static void WrongNameSerialization(Serializer *serializer)
					{
						try
						{
							//Serializing.
							serializer->serializeInt(12, "int_value");
							serializer->serializeBool(true, "bool_value");
							//Deserializing.
							int int_value;
							bool bool_value;
							DeSerializer *deserializer = serializer->createDeSerializer();
							deserializer->deserializeInt(int_value, "int_value");
							deserializer->deserializeBool(bool_value, "float_value");
							Assert::Fail("Error should be thrown");
						}
						catch(ttg::Error &) {}
					}
			};
		}
	}
}
